#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <cstring>

#include <cjson/cJSON.h>

#include "ConfigManager.h"
#include "common.h"
#include "SafeDataFile.h"
#include "Utility.h"


ConfigManager::ConfigManager() {
    loadVersionInfo();
    loadConfigInfo();
    loadExtendDeviceConfig();

    loadNetEnvConfigInfo();
}

ConfigManager::~ConfigManager() {
    //dtor
}



bool ConfigManager::readAndParse(const std::string& pathfile, cJSON*& pRoot) {

    SafeDataFile fileDescriptor(pathfile, O_RDONLY);

    if (!fileDescriptor) {
        return false;
    }

    struct stat statbuff;
    if(fstat(fileDescriptor.getDeviceFd(), &statbuff) < 0) {
        return false;
    }
    char buffer[statbuff.st_size];//
    fileDescriptor.read(buffer, sizeof (buffer));

    fileDescriptor.close();

    pRoot = cJSON_Parse(buffer);

    return true;
}



bool ConfigManager::stringifyAndSaveToFile(cJSON* pRoot, const std::string& pathfile) {
    LOG_INFO("begin for save [%s]", pathfile.data());
    char buffer[MAX_BUFFER_SIZE];
    bool result = static_cast <bool>(cJSON_PrintPreallocated(pRoot, buffer, sizeof (buffer), 1));
    if (result) {
        SafeDataFile fileDescriptor(pathfile, O_WRONLY | O_CREAT, 0666);
        result = (bool)fileDescriptor;
        if (result){
            fileDescriptor.write(buffer, std::strlen(buffer));
            result = fileDescriptor.syncToPersistence();
        }
    }

    cJSON_Delete(pRoot);

    if (!result){
        std::string prefix = pathfile + ",open failed";
        LOG_ERROR(buildErrorMessage(result, errno, prefix).data());
    } else {
        LOG_INFO("save [%s] successfully!", pathfile.data());
    }

    return result;
}



bool ConfigManager::loadVersionInfo() {

    cJSON* pRoot = nullptr;
    bool result = readAndParse(VERSION_CONFIG, pRoot);
    if (!result) {
        return result;
    }
    cJSON* pEntry = nullptr;
    //  LOAD_CONFIG_FIELD(mversionInfo, driverVersionString, "0.0.0");
    LOAD_CONFIG_FIELD(pRoot, mversionInfo, updaterVersionString, "0.0.0");
    LOAD_CONFIG_FIELD(pRoot, mversionInfo, driverVersionString, "0.0.0");
    LOAD_CONFIG_FIELD(pRoot, mversionInfo, kernelVersionString, "0.0.0");
    LOAD_CONFIG_FIELD(pRoot, mversionInfo, filesystemVersionString, "0.0.0");
    LOAD_CONFIG_FIELD(pRoot, mversionInfo, uuidString, DEFAULT_UUID);
    LOAD_CONFIG_FIELD(pRoot, mversionInfo, mac1String, DEFAULT_UUID);
    LOAD_CONFIG_FIELD(pRoot, mversionInfo, mac2String, DEFAULT_UUID);

    cJSON_Delete(pRoot);

    return result;
}



bool  ConfigManager::saveVersionInfo() {

    cJSON* pRoot = cJSON_CreateObject();
    // cJSON_AddStringToObject(pRoot, "driverVersionString", mversionInfo.driverVersionString.c_str());

    SAVE_CONFIG_FIELD(pRoot, mversionInfo, firmwareVersionString);
    SAVE_CONFIG_FIELD(pRoot, mversionInfo, updaterVersionString);
    SAVE_CONFIG_FIELD(pRoot, mversionInfo, driverVersionString);
    SAVE_CONFIG_FIELD(pRoot, mversionInfo, kernelVersionString);
    SAVE_CONFIG_FIELD(pRoot, mversionInfo, filesystemVersionString);
    SAVE_CONFIG_FIELD(pRoot, mversionInfo, uuidString);
    SAVE_CONFIG_FIELD(pRoot, mversionInfo, mac1String);
    SAVE_CONFIG_FIELD(pRoot, mversionInfo, mac2String);

    return stringifyAndSaveToFile(pRoot, VERSION_CONFIG);
}



bool ConfigManager::loadConfigInfo() {
    cJSON* pRoot = nullptr;
    bool result = readAndParse(SYSTEM_CONFIG, pRoot);
    if (!result) {
        return result;
    }
    cJSON* pEntry = nullptr;

    std::string tempValue = LOAD_CONFIG_STRING(pRoot, networkType, std::to_string(NetworkType::ETHERNET));
    mconfigInfo.networkType = (NetworkType)std::stoi(tempValue);

    cJSON* pserverAddress = cJSON_GetObjectItemCaseSensitive(pRoot, "serverAddress");

    LOAD_CONFIG_FIELD(pserverAddress, mconfigInfo.serverAddress, ip, mconfigInfo.serverAddress.ip);

    tempValue = LOAD_CONFIG_STRING(pserverAddress, port, "1983");
    mconfigInfo.serverAddress.port = std::stoi(tempValue);

    LOAD_CONFIG_FIELD(pRoot, mconfigInfo, password, "77884321");

    cJSON_Delete(pRoot);
    return result;
}


bool ConfigManager::saveConfigInfo() {
    cJSON* pRoot = cJSON_CreateObject();

    std::string tempValue = std::to_string(mconfigInfo.networkType);
    SAVE_CONFIG_STRING(pRoot, tempValue, networkType);

    cJSON* pserverAddress = cJSON_AddObjectToObject(pRoot, "serverAddress");

    SAVE_CONFIG_FIELD(pserverAddress, mconfigInfo.serverAddress, ip);

    tempValue = std::to_string(mconfigInfo.serverAddress.port);
    SAVE_CONFIG_STRING(pserverAddress, tempValue, port);

    SAVE_CONFIG_FIELD(pRoot, mconfigInfo, password);

    return stringifyAndSaveToFile(pRoot, SYSTEM_CONFIG);
}



bool ConfigManager::loadExtendDeviceConfig() {

    cJSON* pRoot = nullptr;
    bool result = readAndParse(EXTEND_DEVICE_CONFIG, pRoot);
    if (!result) {
        return result;
    }
    result = parseExtendDeviceCondfig(pRoot);
    cJSON_Delete(pRoot);
    return result;
}



bool ConfigManager::parseExtendDeviceCondfig(cJSON* pRoot) {

    cJSON* pEntry = nullptr;

    cJSON* pElecInfoJson = cJSON_GetObjectItemCaseSensitive(pRoot, "elecInfo");
    if (pElecInfoJson) {
        LOAD_CONFIG_FIELD(pElecInfoJson, mextendDeviceConfig.elecInfo, phaseString, mextendDeviceConfig.elecInfo.phaseString);
        LOAD_CONFIG_INT_FIELD(pElecInfoJson, mextendDeviceConfig.elecInfo, circuitCount, mextendDeviceConfig.elecInfo.circuitCount);
        LOAD_CONFIG_FIELD(pElecInfoJson, mextendDeviceConfig.elecInfo, trippingAlert, mextendDeviceConfig.elecInfo.trippingAlert);
    }

    cJSON* pswitchDetectorInfo = cJSON_GetObjectItemCaseSensitive(pRoot, "switchDetectorInfo");
    if (pswitchDetectorInfo) {
        cJSON* ploopSwitchMapJson = cJSON_GetObjectItemCaseSensitive(pswitchDetectorInfo, "loopSwitchMap");
        if (ploopSwitchMapJson) {
            LoopSwitchMap& loopSwitchMap = mextendDeviceConfig.switchDetectorInfo.loopSwitchMap;
            const LoopSwitchMap::size_type size = loopSwitchMap.size();
            loopSwitchMap.clear();
            loopSwitchMap.resize(size);

            uint16_t index = 0;
            cJSON* element = ploopSwitchMapJson->child;
            cJSON* subElement = nullptr;
            while(element && index < size) {

                if (cJSON_NULL != element->type) {
                    subElement = element->child;
                    while(subElement) {
                        loopSwitchMap[index].emplace_back(subElement->valueint);

                        subElement = subElement->next;
                    }
                }

                ++index;
                element = element->next;
            }
        }
    }

    cJSON* pLoopControllerInfoJson = cJSON_GetObjectItemCaseSensitive(pRoot, "loopControllerInfo");
    if (pLoopControllerInfoJson) {
        LOAD_CONFIG_INT_FIELD(pLoopControllerInfoJson, mextendDeviceConfig.loopControllerInfo, switchInterval, mextendDeviceConfig.loopControllerInfo.switchInterval);
    }

    return true;
}



bool ConfigManager::saveExtendDeviceConfig() {

    cJSON* pRoot = cJSON_CreateObject();
    cJSON* pEntry = nullptr;

    cJSON* pElecInfoJson = cJSON_AddObjectToObject(pRoot, "elecInfo");
    SAVE_CONFIG_FIELD(pElecInfoJson, mextendDeviceConfig.elecInfo, phaseString);
    SAVE_CONFIG_INT_FIELD(pElecInfoJson, mextendDeviceConfig.elecInfo, circuitCount);
    SAVE_CONFIG_FIELD(pElecInfoJson, mextendDeviceConfig.elecInfo, trippingAlert);

    cJSON* pswitchDetectorInfo = cJSON_AddObjectToObject(pRoot, "switchDetectorInfo");
    cJSON* ploopSwitchMapJson = cJSON_AddArrayToObject(pswitchDetectorInfo, "loopSwitchMap");

    LoopSwitchMap& loopSwitchMap = mextendDeviceConfig.switchDetectorInfo.loopSwitchMap;
    cJSON* subArray = nullptr;
    for(LoopSwitchMap::iterator iterator = loopSwitchMap.begin(); iterator !=  loopSwitchMap.end(); ++iterator) {
//        subArray = cJSON_createArray();
        //  for (LoopSwitchMap::value_type::iterator subIterator = iterator->begin(); subIterator != iterator->end(); ++subIterator){

        subArray = cJSON_CreateIntArray(iterator->data(), iterator->size());
        cJSON_AddItemToArray(ploopSwitchMapJson, subArray);

    }

    cJSON* pLoopControllerInfoJson = cJSON_AddObjectToObject(pRoot, "loopControllerInfo");
    SAVE_CONFIG_INT_FIELD(pLoopControllerInfoJson, mextendDeviceConfig.loopControllerInfo, switchInterval);

    return stringifyAndSaveToFile(pRoot, EXTEND_DEVICE_CONFIG);
}



void ConfigManager::parseMap(cJSON* pRoot, Utility::StringMap& map) {
    cJSON* ppairNode = nullptr;
    cJSON_ArrayForEach(ppairNode, pRoot){
        map.emplace(ppairNode->string, ppairNode->valuestring);
    }
}


void ConfigManager::saveMap(cJSON* pRoot, const Utility::StringMap& map) {

}



bool ConfigManager::loadNetEnvConfigInfo() {
    cJSON* pRoot = nullptr;
    bool result = readAndParse(NET_ENV_CONFIG, pRoot);
    if (!result) {
        return result;
    }

    cJSON* pdialScriptsNode = cJSON_GetObjectItemCaseSensitive(pRoot, "dialScripts");
    parseMap(pdialScriptsNode, mnetEnvConfigInfo.dialScripts);

    cJSON* pserverAddrsNode = cJSON_GetObjectItemCaseSensitive(pRoot, "serverAddrs");
    parseMap(pserverAddrsNode, mnetEnvConfigInfo.serverAddrs);

    cJSON_Delete(pRoot);

    return true;
}



bool ConfigManager::saveNetEnvConfigInfo() {



    return true;
}





